/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
 
This file is part of Quake III Arena source code.
 
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
 
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "../client.h"
#include "win_local.h"

WinVars_t	g_wv;

#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST+1)  // message that will be supported by the OS
#endif

static UINT MSH_MOUSEWHEEL;

// Console variables that we need to access from this module
cvar_t		*vid_xpos;			// X coordinate of window position
cvar_t		*vid_ypos;			// Y coordinate of window position
cvar_t		*r_fullscreen;

#define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) )

LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

static qboolean s_alttab_disabled;

static void WIN_DisableAltTab( void )
{
    if ( s_alttab_disabled )
        return;

    if ( !Q_stricmp( Cvar_VariableString( "arch" ), "winnt" ) )
    {
        RegisterHotKey( 0, 0, MOD_ALT, VK_TAB );
    }
    else
    {
        BOOL old;

        SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, &old, 0 );
    }
    s_alttab_disabled = qtrue;
}

static void WIN_EnableAltTab( void )
{
    if ( s_alttab_disabled )
    {
        if ( !Q_stricmp( Cvar_VariableString( "arch" ), "winnt" ) )
        {
            UnregisterHotKey( 0, 0 );
        }
        else
        {
            BOOL old;

            SystemParametersInfo( SPI_SCREENSAVERRUNNING, 0, &old, 0 );
        }

        s_alttab_disabled = qfalse;
    }
}

/*
==================
VID_AppActivate
==================
*/
static void VID_AppActivate(BOOL fActive, BOOL minimize)
{
    g_wv.isMinimized = minimize;

    Com_DPrintf("VID_AppActivate: %i\n", fActive );

    Key_ClearStates();	// FIXME!!!

    // we don't want to act like we're active if we're minimized
    if (fActive && !g_wv.isMinimized )
    {
        g_wv.activeApp = qtrue;
    }
    else
    {
        g_wv.activeApp = qfalse;
    }

    // minimize/restore mouse-capture on demand
    if (!g_wv.activeApp )
    {
        IN_Activate (qfalse);
    }
    else
    {
        IN_Activate (qtrue);
    }
}

//==========================================================================

static byte s_scantokey[128] =
    {
        //  0           1       2       3       4       5       6       7
        //  8           9       A       B       C       D       E       F
        0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6',
        '7',    '8',    '9',    '0',    '-',    '=',    K_BACKSPACE, 9, // 0
        'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
        'o',    'p',    '[',    ']',    13 ,    K_CTRL,'a',  's',      // 1
        'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
        '\'' ,    '`',    K_SHIFT,'\\',  'z',    'x',    'c',    'v',      // 2
        'b',    'n',    'm',    ',',    '.',    '/',    K_SHIFT,'*',
        K_ALT,' ',   K_CAPSLOCK  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3
        K_F6, K_F7, K_F8, K_F9, K_F10,  K_PAUSE,    0  , K_HOME,
        K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4
        K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11,
        K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
        0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
        0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6
        0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
        0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7
    };

/*
=======
MapKey
 
Map from windows to quake keynums
=======
*/
static int MapKey (int key)
{
    int result;
    int modified;
    qboolean is_extended;

//	Com_Printf( "0x%x\n", key);

    modified = ( key >> 16 ) & 255;

    if ( modified > 127 )
        return 0;

    if ( key & ( 1 << 24 ) )
    {
        is_extended = qtrue;
    }
    else
    {
        is_extended = qfalse;
    }

    result = s_scantokey[modified];

    if ( !is_extended )
    {
        switch ( result )
        {
        case K_HOME:
            return K_KP_HOME;
        case K_UPARROW:
            return K_KP_UPARROW;
        case K_PGUP:
            return K_KP_PGUP;
        case K_LEFTARROW:
            return K_KP_LEFTARROW;
        case K_RIGHTARROW:
            return K_KP_RIGHTARROW;
        case K_END:
            return K_KP_END;
        case K_DOWNARROW:
            return K_KP_DOWNARROW;
        case K_PGDN:
            return K_KP_PGDN;
        case K_INS:
            return K_KP_INS;
        case K_DEL:
            return K_KP_DEL;
        default:
            return result;
        }
    }
    else
    {
        switch ( result )
        {
        case K_PAUSE:
            return K_KP_NUMLOCK;
        case 0x0D:
            return K_KP_ENTER;
        case 0x2F:
            return K_KP_SLASH;
        case 0xAF:
            return K_KP_PLUS;
        }
        return result;
    }
}


/*
====================
MainWndProc
 
main window procedure
====================
*/
extern cvar_t *in_mouse;
extern cvar_t *in_logitechbug;
LONG WINAPI MainWndProc (
    HWND    hWnd,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam)
{
    static qboolean flip = qtrue;
    int zDelta, i;

    // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/aboutmouseinput.asp
    // Windows 95, Windows NT 3.51 - uses MSH_MOUSEWHEEL
    // only relevant for non-DI input
    //
    // NOTE: not sure how reliable this is anymore, might trigger double wheel events
    if (in_mouse->integer != 1)
    {
        if ( uMsg == MSH_MOUSEWHEEL )
        {
            if ( ( ( int ) wParam ) > 0 )
            {
                Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
                Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
            }
            else
            {
                Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
                Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
            }
            return DefWindowProc (hWnd, uMsg, wParam, lParam);
        }
    }

    switch (uMsg)
    {
    case WM_MOUSEWHEEL:
        // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/aboutmouseinput.asp
        // Windows 98/Me, Windows NT 4.0 and later - uses WM_MOUSEWHEEL
        // only relevant for non-DI input and when console is toggled in window mode
        //   if console is toggled in window mode (KEYCATCH_CONSOLE) then mouse is released and DI doesn't see any mouse wheel
        if (in_mouse->integer != 1 || (!r_fullscreen->integer && (cls.keyCatchers & KEYCATCH_CONSOLE)))
        {
            // 120 increments, might be 240 and multiples if wheel goes too fast
            // NOTE Logitech: logitech drivers are screwed and send the message twice?
            //   could add a cvar to interpret the message as successive press/release events
            zDelta = ( short ) HIWORD( wParam ) / 120;
            if ( zDelta > 0 )
            {
                for(i=0; i<zDelta; i++)
                {
                    if (!in_logitechbug->integer)
                    {
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
                    }
                    else
                    {
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, flip, 0, NULL );
                        flip = !flip;
                    }
                }
            }
            else
            {
                for(i=0; i<-zDelta; i++)
                {
                    if (!in_logitechbug->integer)
                    {
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
                    }
                    else
                    {
                        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, flip, 0, NULL );
                        flip = !flip;
                    }
                }
            }
            // when an application processes the WM_MOUSEWHEEL message, it must return zero
            return 0;
        }
        break;

    case WM_CREATE:

        g_wv.hWnd = hWnd;

        vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
        vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
        r_fullscreen = Cvar_Get ("r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH );

        MSH_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG");
        if ( r_fullscreen->integer )
        {
            WIN_DisableAltTab();
        }
        else
        {
            WIN_EnableAltTab();
        }

        break;
#if 0
    case WM_DISPLAYCHANGE:
        Com_DPrintf( "WM_DISPLAYCHANGE\n" );
        // we need to force a vid_restart if the user has changed
        // their desktop resolution while the game is running,
        // but don't do anything if the message is a result of
        // our own calling of ChangeDisplaySettings
        if ( com_insideVidInit )
        {
            break;		// we did this on purpose
        }
        // something else forced a mode change, so restart all our gl stuff
        Cbuf_AddText( "vid_restart\n" );
        break;
#endif
    case WM_DESTROY:
        // let sound and input know about this?
        g_wv.hWnd = NULL;
        if ( r_fullscreen->integer )
        {
            WIN_EnableAltTab();
        }
        break;

    case WM_CLOSE:
        Cbuf_ExecuteText( EXEC_APPEND, "quit" );
        break;

    case WM_ACTIVATE:
    {
        int	fActive, fMinimized;

        fActive = LOWORD(wParam);
        fMinimized = (BOOL) HIWORD(wParam);

        VID_AppActivate( fActive != WA_INACTIVE, fMinimized);
        SNDDMA_Activate();
    }
    break;

    case WM_MOVE:
    {
        int		xPos, yPos;
        RECT r;
        int		style;

        if (!r_fullscreen->integer )
        {
            xPos = (short) LOWORD(lParam);    // horizontal position
            yPos = (short) HIWORD(lParam);    // vertical position

            r.left   = 0;
            r.top    = 0;
            r.right  = 1;
            r.bottom = 1;

            style = GetWindowLong( hWnd, GWL_STYLE );
            AdjustWindowRect( &r, style, FALSE );

            Cvar_SetValue( "vid_xpos", xPos + r.left);
            Cvar_SetValue( "vid_ypos", yPos + r.top);
            vid_xpos->modified = qfalse;
            vid_ypos->modified = qfalse;
            if ( g_wv.activeApp )
            {
                IN_Activate (qtrue);
            }
        }
    }
    break;

// this is complicated because Win32 seems to pack multiple mouse events into
// one update sometimes, so we always check all states and look for events
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_MOUSEMOVE:
    {
        int	temp;

        temp = 0;

        if (wParam & MK_LBUTTON)
            temp |= 1;

        if (wParam & MK_RBUTTON)
            temp |= 2;

        if (wParam & MK_MBUTTON)
            temp |= 4;

        IN_MouseEvent (temp);
    }
    break;

    case WM_SYSCOMMAND:
        if ( wParam == SC_SCREENSAVE )
            return 0;
        break;

    case WM_SYSKEYDOWN:
        if ( wParam == 13 )
        {
            if ( r_fullscreen )
            {
                Cvar_SetValue( "r_fullscreen", !r_fullscreen->integer );
                Cbuf_AddText( "vid_restart\n" );
            }
            return 0;
        }
        // fall through
    case WM_KEYDOWN:
        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, MapKey( lParam ), qtrue, 0, NULL );
        break;

    case WM_SYSKEYUP:
    case WM_KEYUP:
        Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, MapKey( lParam ), qfalse, 0, NULL );
        break;

    case WM_CHAR:
        Sys_QueEvent( g_wv.sysMsgTime, SE_CHAR, wParam, 0, 0, NULL );
        break;
    }

    return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

